package com.kefu.robot.service;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.kefu.chat.mapper.ChatFaqGroupDocRelMapper;
import com.kefu.chat.model.ChatFaqGroupDocRel;
import com.kefu.chat.model.ChatSessionMsg;
import com.kefu.chat.service.ChatSessionMsgService;
import com.kefu.common.mybatis.Order;
import com.kefu.robot.mapper.RobotDocCommentMapper;
import com.kefu.robot.model.*;
import com.kefu.robot.vo.*;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.kefu.chat.http.NlpClient;
import com.kefu.chat.http.UpdateDocParam;
import com.kefu.common.context.KefuContext;
import com.kefu.common.service.AbstractKefuService;
import com.kefu.robot.mapper.RobotDocLinkMapper;
import com.kefu.robot.mapper.RobotDocMapper;
import com.kefu.robot.mapper.RobotDocQuestionMapper;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;

import javax.annotation.Resource;

@Service
public class RobotDocService  extends AbstractKefuService<RobotDoc> {
    protected Logger logger = LoggerFactory.getLogger(getClass());
    @Resource
    RobotDocMapper robotDocMapper;
    @Resource
    RobotDocLinkMapper robotDocLinkMapper;
    @Autowired
    RobotDocTypeService robotDocTypeService;
    @Autowired
    NlpClient nlpClient;
    @Autowired
    RobotDocQuestionService robotDocQuestionService;
    @Resource
    RobotDocCommentMapper robotDocCommentMapper;
    @Autowired
    ChatSessionMsgService chatSessionMsgService;
    @Resource
    RobotDocQuestionMapper robotDocQuestionMapper;
    @Resource
    ChatFaqGroupDocRelMapper chatFaqGroupDocRelMapper;


    public List<RobotDoc> searchJoin(RobotDocSearchVo search) {
        // 将分类节点下的所有子节点都查询出来
        if (StrUtil.isNotBlank(search.getDocTypeId())) {
            search.setDocTypeIdList(robotDocTypeService.getAllNodes(search.getDocTypeId()));
        }
        return robotDocMapper.searchJoin(search);
    }

    
    public String addDoc(RobotDocInsertVo docVo) {
        RobotDoc robotDoc = docVo.convert();
        long time = System.currentTimeMillis();
        if (docVo.getTimeMode() != null && docVo.getTimeMode() == 1 && (time < docVo.getStartTime() || time > docVo.getEndTime())) {
            robotDoc.setDocStatus(0);
        }
        save(robotDoc);
        if (CollUtil.isNotEmpty(docVo.getSimilarQuestions())) {
            for (RobotDocQuestion similarQuestion : docVo.getSimilarQuestions()) {
                if (StrUtil.isNotBlank(similarQuestion.getQuestion())) {
                    similarQuestion.setId(IdUtil.simpleUUID());
                    similarQuestion.setDocId(robotDoc.getDocId());
                    robotDocQuestionMapper.insert(similarQuestion);
                }
            }
        }
        if (CollUtil.isNotEmpty(docVo.getLinkQuestions())) {
            for (RobotDocLink link : docVo.getLinkQuestions()) {
                link.setDocId(robotDoc.getDocId());
                robotDocLinkMapper.insert(link);
            }
        }

        return robotDoc.getDocId();
    }

    public void copyDoc(RobotCopyDocVo vo) {
        vo.getDocIdList().forEach(docId -> {
            RobotDoc oldDoc = getById(docId);
            String newDocId = IdUtil.simpleUUID();
            RobotDoc newDoc = BeanUtil.toBean(oldDoc,RobotDoc.class);
            newDoc.setDocId(newDocId);
            newDoc.setDocTypeId(vo.getDocTypeId());
            newDoc.setTenantId(KefuContext.getTid());
            newDoc.setCreateTime(System.currentTimeMillis());
            newDoc.setUpdateTime(newDoc.getCreateTime());
            newDoc.setCreateUser(KefuContext.getUid());
            newDoc.setUpdateUser(newDoc.getCreateUser());
            save(newDoc);
            List<RobotDocQuestion> oldQuestionList = robotDocQuestionMapper.selectList(
                    Wrappers.lambdaQuery(RobotDocQuestion.class).eq(RobotDocQuestion::getDocId,docId));
            oldQuestionList.forEach(question->{
                RobotDocQuestion newQuestion = BeanUtil.toBean(question,RobotDocQuestion.class);
                newQuestion.setId(IdUtil.simpleUUID());
                newQuestion.setDocId(newDocId);
                robotDocQuestionMapper.insert(newQuestion);
            });
        });
    }

    
    public String updateDoc(RobotDocUpdateVo docVo) {
        RobotDoc robotDoc = docVo.convert();
        if (docVo.getSimilarUpdate() == 1) {
            if(CollUtil.isNotEmpty(docVo.getSimilarQuestions())){
                robotDocQuestionMapper.deleteByDocId(docVo.getDocId());
                for (RobotDocQuestion similarQuestion : docVo.getSimilarQuestions()) {
                    if (StrUtil.isNotBlank(similarQuestion.getQuestion())) {
                        similarQuestion.setId(IdUtil.simpleUUID());
                        similarQuestion.setDocId(robotDoc.getDocId());
                    }
                }
                robotDocQuestionService.saveBatch(docVo.getSimilarQuestions());
            }

        }
        if (docVo.getLinkUpdate() == 1) {
            robotDocLinkMapper.deleteByDocId(docVo.getDocId());
            for (RobotDocLink link : docVo.getLinkQuestions()) {
                link.setDocId(robotDoc.getDocId());
                robotDocLinkMapper.insert(link);
            }
        }
        update(robotDoc);

        return "1";
    }

    
    public String updateDocBatch(RobotDocUpdateBatchVo robotDocVo) {
        robotDocVo.getDocIdList().forEach(docId -> {
            RobotDoc doc = new RobotDoc();
            doc.setDocId(docId);
            if (robotDocVo.getUpdateType()==0){
                doc.setDocStatus(0);
                update(doc);
            } else if (robotDocVo.getUpdateType()==1){
                doc.setDocStatus(1);
                update(doc);
            }else if (robotDocVo.getUpdateType()==2){
                doc.setDocTypeId(robotDocVo.getDocTypeId());
                update(doc);
            }else if (robotDocVo.getUpdateType()==3){
                delDoc(docId);
            }
        });
        return "1";
    }

    
    public void delDoc(String docId) {
        deleteById(docId);
        robotDocQuestionMapper.deleteByDocId(docId);
        robotDocLinkMapper.deleteByDocId(docId);
        // 删除词条关联的常见问题
        chatFaqGroupDocRelMapper.delete(Wrappers.lambdaQuery(ChatFaqGroupDocRel.class).eq(ChatFaqGroupDocRel::getDocId, docId));
    }


    public List<RobotDocQuestion> getSimilarQuestions(String docId) {
        RobotDocQuestion question = new RobotDocQuestion();
        question.setDocId(docId);
        return robotDocQuestionMapper.select(question);
    }

    
    public List<RobotDocLink> getLinkQuestions(String docId) {
        return robotDocLinkMapper.queryList(docId);
    }

    
    public String importDoc(InputStream inputStream, Integer robotNo) {
        ExcelReader excelReader = ExcelUtil.getReader(inputStream);
        List<Map<String, Object>> list = excelReader.read(0, 1, 10000);
        RobotDocType docTypeModel = new RobotDocType();
        String tenantId = KefuContext.getKefuContext().getTenantId();
        docTypeModel.setTenantId(tenantId);
        docTypeModel.setRobotNo(robotNo);
        List<RobotDocType> docTypeList = robotDocTypeService.findByModel(docTypeModel);
        List<RobotDocInsertVo> dbList = Lists.newArrayList();
        for (int i =0; i< list.size(); i++) {
            try {
                Map<String, Object> map = list.get(i);
                String question = String.valueOf(map.get("标准问题"));
                String answer = String.valueOf(map.get("标准答案"));
                String questionSim = String.valueOf(map.get("相似问法"));
                String typeName = String.valueOf(map.get("分类名称"));
                String status = String.valueOf(map.get("启用状态"));
                if (StrUtil.isBlankOrUndefined(question)) {
                    logger.info("导入错误:{},{},{}", i + 1, question, answer);
                    continue;
                }
                RobotDocInsertVo insertVo = new RobotDocInsertVo();
                insertVo.setRobotNo(robotNo);
                insertVo.setDocQuestion(question.replaceAll("\r|\n", "").trim());
                insertVo.setDocAnswer(answer);
                insertVo.setDocStatus(1);
                insertVo.setTimeMode(0);
                insertVo.setDocPattern(0);
                if(robotNo==-10) {
                	insertVo.setDocAnswer(String.format("[{\"type\":2,\"content\":\"%s\"}]", answer));
                }
                if (!StrUtil.isBlankOrUndefined(questionSim)) {
                    List<String> qList = StrSplitter.split(questionSim.replaceAll("\r|\n", ""), '~', 0, true, true);
                    insertVo.setSimilarQuestions(Sets.newHashSet());
                    for (String q : qList) {
                        RobotDocQuestion docQuestion = new RobotDocQuestion();
                        docQuestion.setQuestion(q);
                        insertVo.getSimilarQuestions().add(docQuestion);
                    }
                }
                if (!StrUtil.isBlankOrUndefined(typeName)) {
                    typeName = typeName.trim();
                    for (RobotDocType d : docTypeList) {
                        if (typeName.equals(d.getName())) {
                            insertVo.setDocTypeId(d.getId());
                            break;
                        }
                    }
                    //如果分类名称不存在，添加分类入库
                    if (insertVo.getDocTypeId() == null) {
                        RobotDocType docTypeInsert = new RobotDocType();
                        docTypeInsert.setId(IdUtil.simpleUUID());
                        docTypeInsert.setTenantId(tenantId);
                        docTypeInsert.setRobotNo(robotNo);
                        docTypeInsert.setParentId("0");
                        docTypeInsert.setName(typeName);
                        docTypeInsert.setWeight(robotDocTypeService.getMaxWeight(tenantId, "0") + 1);
                        docTypeInsert.setCreateTime(System.currentTimeMillis());
                        docTypeInsert.setCreateUser(KefuContext.getKefuContext().getUserId());
                        robotDocTypeService.save(docTypeInsert);
                        insertVo.setDocTypeId(docTypeInsert.getId());
                        docTypeList.add(docTypeInsert);
                    }
                }
                if (!StrUtil.isBlankOrUndefined(status) && "停用".equals(status)) {
                    insertVo.setDocStatus(0);
                }
                dbList.add(insertVo);
            } catch (Exception e) {e.printStackTrace(); throw e;}
        }
        if (dbList.size() > 0) dbList.forEach(insertVo -> {addDoc(insertVo);});
        return "1";
    }

    
    public boolean save(RobotDoc model) {
        if (1 == model.getDocStatus()) {
            nlpClient.updateDoc(UpdateDocParam.builder().docId(model.getDocId()).updateType(1).build());
        }
        return super.save(model);
    }

    
    public RobotDoc update(RobotDoc model) {
        nlpClient.updateDoc(UpdateDocParam.builder().docId(model.getDocId()).updateType(2).build());
        return super.update(model);
    }

    
    public void deleteById(String id) {
        nlpClient.updateDoc(UpdateDocParam.builder().docId(id).updateType(3).tenantId(KefuContext.getKefuContext().getTenantId()).build());
        super.deleteById(id);
    }

    public List<Map<String, Object>> exportDoc(RobotDocSearchVo search) {
        search.setTenantId(KefuContext.getKefuContext().getTenantId());
        search.setOrderField("update_time");
        search.setOrder(Order.DESC);
        List<RobotDoc> list = searchJoin(search);
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (RobotDoc doc : list) {
            Map<String, Object> map = Maps.newLinkedHashMap();
            map.put(msg("标准问题"), doc.getDocQuestion());
            map.put(msg("相似问法"), doc.getQuestions());
            String answer = doc.getDocAnswer();
            //Excel单元格不能太长，如果超长，过滤html，如果还超长，截取字符串
            if (StrUtil.isNotBlank(answer)) {
                answer = answer.length() > 32767 ? answer.replaceAll("<([^>]*)>", "") : answer;
            }
            if (StrUtil.isNotBlank(answer) && answer.length() > 32767) {
                answer = answer.substring(0, 32767);
            }
            map.put(msg("标准答案"), answer);
            String docTypeId = doc.getDocTypeId();
            String docTypeName = doc.getDocTypeName();
            RobotDocType robotDocType = robotDocTypeService.findById(docTypeId);
            if (Objects.nonNull(robotDocType)) {
                List<RobotDocType> robotDocTypeList = Lists.newArrayList();
                robotDocTypeService.getParent(robotDocType, robotDocTypeList);
                if (CollUtil.isNotEmpty(robotDocTypeList)) {
                    docTypeName = robotDocTypeList.stream().map(RobotDocType::getName).collect(Collectors.joining("/"));
                }
            }
            map.put(msg("分类名称"), docTypeName);
            map.put(msg("启用状态"), msg("启用"));
            if (doc.getDocStatus() != null && doc.getDocStatus() == 0) {
                map.put(msg("启用状态"), msg("停用"));
            }
            mapList.add(map);
        }
        return mapList;
    }

    public Page<RobotDoc> searchList(RobotDocSearchVo search) {
        //查询词条点赞数据
        QueryWrapper<RobotDocComment> wrapper = new QueryWrapper<RobotDocComment>();
        wrapper.select("count(*) as count,doc_id");
        wrapper.groupBy("doc_id");
        wrapper.eq("is_useful", 0);
        wrapper.eq("tenant_id", KefuContext.getKefuContext().getTenantId());
        List<RobotDocComment> isUseful = robotDocCommentMapper.selectList(wrapper);
        Map<String, Integer> isUsefulMap = isUseful.stream().filter(robotDocComment -> StringUtils.isNotBlank(robotDocComment.getDocId())).collect(Collectors.toMap(RobotDocComment::getDocId, RobotDocComment::getCount));
        //清楚条件构造器条件
        wrapper.clear();
        //查询词条点踩数据
        wrapper.select("count(*) as count,doc_id");
        wrapper.groupBy("doc_id");
        wrapper.eq("tenant_id", KefuContext.getKefuContext().getTenantId());
        wrapper.eq("is_useful", 1);
        List<RobotDocComment> isNotUseful = robotDocCommentMapper.selectList(wrapper);
        Map<String, Integer> isNotUsefulMap = isNotUseful.stream().filter(robotDocComment -> StringUtils.isNotBlank(robotDocComment.getDocId())).collect(Collectors.toMap(RobotDocComment::getDocId, RobotDocComment::getCount));
        //获取词条命中情况
        QueryWrapper<ChatSessionMsg> docHitWrapper = new QueryWrapper<ChatSessionMsg>();
        docHitWrapper.select("count(*) as count,doc_id");
        docHitWrapper.groupBy("doc_id");
        docHitWrapper.isNotNull("doc_id");
        docHitWrapper.eq("tenant_id", KefuContext.getKefuContext().getTenantId());
        List<ChatSessionMsg> docHitList = chatSessionMsgService.list(docHitWrapper);
        Map<String, Integer> docHitMap = docHitList.stream().filter(chatRptDocHit -> StringUtils.isNotBlank(chatRptDocHit.getDocId())).collect(Collectors.toMap(ChatSessionMsg::getDocId, ChatSessionMsg::getCount));
        Page<RobotDoc> list = search(search);
//        Map<String, String> docTypeNameMap = robotDocTypeService.getDocTypeNameMap(KefuContext.getKefuContext().getTenantId());
        list.getRecords().forEach(item -> {
            item.setIsUseful(isUsefulMap.get(item.getDocId()));
            item.setIsNotUseful(isNotUsefulMap.get(item.getDocId()));
            item.setDocHit(docHitMap.get(item.getDocId()));
//            item.setDocTypeName(docTypeNameMap.get(item.getDocTypeId()));
            String docTypeId = item.getDocTypeId();
            RobotDocType robotDocType = robotDocTypeService.findById(docTypeId);
            if (Objects.nonNull(robotDocType)) {
                List<RobotDocType> robotDocTypeList = Lists.newArrayList();
                robotDocTypeService.getParent(robotDocType, robotDocTypeList);
                if (CollUtil.isNotEmpty(robotDocTypeList)) {
                    item.setDocTypeName(robotDocTypeList.stream().map(RobotDocType::getName).collect(Collectors.joining("/")));
                }
            }
            if (Objects.equals(1, search.getSimilarQuestionsFlag())) {
                item.setSimilarQuestions(getSimilarQuestions(item.getDocId()));
            }
        });
        return list;
    }
}
